home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
SourceCode
/
TreeView
/
TreeController.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
7KB
|
199 lines
#import "TreeController.h"
#import "NamedTree.h"
#import "TreeView.h"
@implementation TreeController
- init
{
[super init];
first = YES;
nextX = 200;
nextY = 600;
return self;
}
- info:sender // bring up the info panel, obviously
{
if(!infoPanel)
[NXApp loadNibSection:"InfoPanel.nib" owner:self withNames:NO];
return [infoPanel orderFront:sender];
}
- open:sender
{ // use open panel to select a file -- only with .tree extension.
// only one file may be loaded at a time.
const char *const *files;
char *file;
const char *dir;
static const char *const ft[2] = {"tree", NULL};
id openPanel = [OpenPanel new];
[openPanel allowMultipleFiles:NO];
if (first) {
[openPanel runModalForDirectory:[[NXBundle mainBundle] directory]
file:NULL types:ft];
first = NO;
} else [openPanel runModalForTypes:ft];
files = [openPanel filenames];
dir = [openPanel directory];
file = malloc(strlen(files[0]) + strlen(dir) + 8);
strcpy(file, dir);
strcat(file,"/");
strcat(file, files[0]);
strcat(file, "\0");
[self openFile:file];
return self;
}
char nextChar; // This allows me to do single character lookahead in parse
id readToNewline(FILE *file) // used to parse a file; reads a line at a time
{ // returns a string object... reads until EOL to get string value.
id newString = [[String alloc] init];
char *buffer = (char *)malloc(1024); // should be plenty big
char *spot = buffer;
while (nextChar != '\n') {
spot[0] = nextChar; spot++; nextChar = fgetc(file);
}
spot[0] = '\0'; // terminate the string
nextChar = fgetc(file);
[newString setString:buffer];
free(buffer);
return newString;
}
// This actually opens a file. WorkSpace and Open panel methods both
// eventually get to here to do the real work. This code is pretty much
// worth ignoring, unless you _really_ care about how I read in the
// files. It's mostly specific to the file format so it's not
// generally useful. The framework for this code came from the
// code in my "Viewer.app" that is in with some PD raytracers I ported
// to the NeXT--allows viewing an rgb bitmap file; I wrote it before
// GW and ImageViewer existed... (See raytracers.tar.Z on sonata/orst)
- (BOOL)openFile:(const char *)name
{
id alert, aString, treeName, rootNode, workingNode, tempNode;
id newString, stack = [[List alloc] init];
int indLevel, numSpaces, indent = 0;
char *tempString;
BOOL rStat = YES;
FILE *file;
// for debugging:
//NXStream *out = NXOpenFile(fileno(stdout), NX_WRITEONLY);
// get a new doc window.
[NXApp loadNibSection:"DocWindow.nib" owner:self withNames:NO];
[[treeView window] setTitleAsFilename:name];
// put up an alert panel to let user know we're busy
alert = NXGetAlertPanel(NULL, "Reading tree and creating image.",
NULL, NULL, NULL);
[alert makeKeyAndOrderFront:self];
// Read the tree file. Note that I don't do error checking. Badness.
file = fopen(name, "r");
nextChar = fgetc(file); // prime the system
// first line is tree's name.
treeName = readToNewline(file);
aString = readToNewline(file); // get the name of the root node.
rootNode = [[[NamedTree alloc]
initLabelString:aString] setTreeName:treeName];
[stack insertObject:rootNode at:0];
workingNode = rootNode;
// figure out the indentation
while (nextChar == ' ') {
indent++;
nextChar = fgetc(file);
}
aString = readToNewline(file); // get name of child node
tempNode = [[[NamedTree alloc]
initLabelString:aString] setTreeName:treeName];
[workingNode addBranch:tempNode];
[stack insertObject:tempNode at:0];
workingNode = tempNode;
// now that we know the file's char's, we read in the other nodes
// I use a List object as if it were a stack and push parent nodes on
// it while working on children rather than doing a recursive function.
// the comments are sparse, just know that it's mostly pushing and
// popping from the stack to get at the right parent to add a child to.
while (!feof(file)) {
aString = readToNewline(file); // next node name + indentation.
// find out # of indentation spaces and strip them off
// *** this like gives a warning: ignore it, it's unimportant here.
tempString = [aString stringValue]; numSpaces = 0;
while (tempString[0] == ' ') {
numSpaces++; tempString++;
}
indLevel = numSpaces / indent;
if (indLevel == ([stack count] - 1)) { // same level as last object
[stack removeObjectAt:0];
workingNode = [stack objectAt:0];
newString = [[String alloc] initString:tempString];
[aString free];
tempNode = [[[NamedTree alloc]
initLabelString:newString] setTreeName:treeName];
[workingNode addBranch:tempNode];
[stack insertObject:tempNode at:0];
workingNode = tempNode;
} else if (indLevel == ([stack count])) { // child of last node
newString = [[String alloc] initString:tempString];
[aString free];
tempNode = [[[NamedTree alloc]
initLabelString:newString] setTreeName:treeName];
[workingNode addBranch:tempNode];
[stack insertObject:tempNode at:0];
workingNode = tempNode;
} else if (indLevel < [stack count]) { // higher level, so pop
while (indLevel < [stack count]) { // pop until at right level
[stack removeObjectAt:0];
workingNode = [stack objectAt:0];
} // now add new node since we're at the level
newString = [[String alloc] initString:tempString];
[aString free];
tempNode = [[[NamedTree alloc]
initLabelString:newString] setTreeName:treeName];
[workingNode addBranch:tempNode];
[stack insertObject:tempNode at:0];
workingNode = tempNode;
} else { // typically, if user goes in two levels at once, which
// doesn't make any sense semantically
fprintf(stderr, "Error: level too deep!\n");
rStat = NO;
}
}
// Debugging code to pretty print the parsed tree. If this output
// is correct, then we know that the parse was OK.
//printf("\nHere's the parsed tree:\n");
//printf("Tree name: \"%s\".", [treeName stringValue]);
//[rootNode dumpTree:out level:0 indent:" "];
//printf("\n\n");
//NXClose(out);
// end debug code
// rStat = return status of tree reader YES = success
// Now attach the Tree to the TreeView...
[treeView attachTree:rootNode];
// and now bring up the window for the user and get rid of the alert
[[treeView window] moveTo:nextX :(nextY-218)];
[alert orderOut:self];
[alert free];
nextX += 22; nextY -= 25;
if (nextX > 370) {
nextX = 200; nextY = 600;
}
[[treeView window] makeKeyAndOrderFront:self];
return rStat;
}
// The next two methods allow the WorkSpace to open a .tree file when
// it is double-clicked. (Or any file that's cmd-dragged over our icon.)
- (BOOL)appAcceptsAnotherFile:sender { return YES; }
- (int)app:sender openFile:(const char *)file type:(const char *)type
{
return [self openFile:file];
}
@end